/*
*  Vector3.h
	*  RTDemo
	*
	*  Created by steveca  on 27/04/09.
	*  Copyright 2009 __MyCompanyName__. All rights reserved.
	*
*/
#ifndef __RTRT_VECTOR3_H__
#define __RTRT_VECTOR3_H__

#include <assert.h>
#include <math.h>

namespace RTRT 
{
	class Vector3 
	{
	public:
		inline Vector3() 
			: x(0), y(0), z(0)
			{}

		inline Vector3(const float _x, const float _y, const float _z) 
			: x(_x), y(_y), z(_z)
		{
		}

		inline explicit Vector3(const float coordinates[3])
			: x(coordinates[0]), y(coordinates[1]), z(coordinates[2])
		{
		}

		inline explicit Vector3(const int coordinates[3])
			: x((float)coordinates[0]),y((float)coordinates[1]),z((float)coordinates[2])
		{
		}

		inline explicit Vector3(float *const v)
			: x(v[0]), y(v[1]), z(v[2])
		{
		}

		inline explicit Vector3(const float scaler)
			: x(scaler), y(scaler), z(scaler)
		{
		}

		inline float operator [] (const size_t i) const
		{
			assert(i<3);
			return *(&x + i);
		}

		inline float operator [] (const size_t i) 
		{
			assert(i<3);
			return *(&x + i);
		}

		inline float cell(size_t i)
		{
			assert(i<3);
			return *(&x + i);
		}

		inline const float* ptr() const
		{
			return &x;
		}

		inline const float* ptr()
		{
			return &x;
		}

		inline Vector3& operator = (const Vector3& v)
		{
			x = v.x;
			y = v.y;
			z = v.z;

			return *this;
		}

		inline Vector3& operator = (const float scaler)
		{
			x = scaler;
			y = scaler;
			z = scaler;

			return *this;
		}

		inline bool operator == (const Vector3& v) const
		{
			return (x == v.x && y == v.y && z == v.z);
		}

		inline bool operator != (const Vector3& v) const
		{
			return (x != v.x  || y != v.y || z != v.z);
		}

		inline Vector3 operator + (const Vector3& v) const
		{
			return Vector3(x + v.x, y + v.y, z + v.z);
		}

		inline Vector3 operator - (const Vector3& v) const
		{
			return Vector3(x - v.x, y - v.y, z - v.z);
		}

		inline Vector3 operator * (const float scaler) const
		{
			return Vector3(x * scaler, y *scaler, z * scaler);
		}

		inline Vector3 operator * (const Vector3& v) const
		{
			return Vector3(x * v.x, y * v.y, z * v.z);
		}

		inline Vector3 operator / (const float scaler) const
		{
			assert(scaler != 0);
			float inv = 1.0f / scaler;

			return Vector3(x * inv, y * inv, z * inv);
		}

		inline Vector3 operator / (const Vector3& v) const
		{
			return Vector3(x / v.x, y / v.y, z / v.z);
		}

		inline Vector3& operator + () const
		{
			return *this;
		}

		inline Vector3 operator - () const
		{
			return Vector3(-x, -y, -z);
		}

		inline friend Vector3 operator * (const float scaler, const Vector3& v)
		{
			return Vector3(scaler * v.x, scaler * v.y, scaler * v.z);
		}

		inline friend Vector3 operator / (const float scaler, const Vector3& v)
		{
			return Vector3( scaler / v.x, scaler / v.y, scaler / v.z);
		}

		inline friend Vector3 operator + (const float lhs, const Vector3& v)
		{
			return Vector3(lhs + v.x, lhs + v.y, lhs + v.z);
		}

		inline friend Vector3 operator + (const Vector3& v, const float rhs)
		{
			return Vector3(v.x + rhs, v.y + rhs, v.z + rhs);
		}

		inline friend Vector3 operator - (const float lhs, const Vector3& v)
		{
			return Vector3(lhs - v.x, lhs - v.y, lhs - v.z);
		}

		inline friend Vector3 operator + (const Vector3& v, const float rhs)
		{
			return Vector3(v.x - rhs, v.y - rhs, v.z - rhs);
		}

		inline Vector3& operator += (const Vector3& v)
		{
			x += v.x;
			y += v.y;
			z += v.z;

			return *this;
		}

		inline Vector3& operator -= (const Vector3& v)
		{
			x -= v.x;
			y -= v.y;
			z -= v.z;

			return *this;
		}

		inline Vector3& operator += (const float scaler)
		{
			x += scaler;
			y += scaler;
			z += scaler;

			return *this
			}

			inline Vector3& operator -= (const float scaler)
			{
				x -= scaler;
				y -= scaler;
				z -= scaler;

				return *this;
			}

			inline Vector3& operator *= (const Vector3& v)
			{
				x *= v.x;
				y *= v.y;
				z *= v.z;   

				return *this;
			}

			inline Vector3& operator *= (const float scaler)
			{
				x *= scaler;
				y *= scaler;
				z *= scaler;

				return *this;
			}


			inline float length() const
			{
				return sqrt(x * x + y * y + z * z);
			}

			inline float squaredLength() const
			{
				return x * x + y * y + z * z;
			}

			inline float distance(const Vector3& v) const
			{
				return (*this - v).length();
			}

			inline float squaredDistance(const Vector3& v) const
			{
				return (*this - v).lengthSquare();
			}

			inline float dotProduct(const Vector3& v) const
			{
				return x * v.x + y * v.y + z * v.z;
			}

			inline float absDotProduct(const Vector3& v) const
			{
				return abs(x * v.x) + abs(y * v.y) + abs(z * v.z);
			}

			inline float normalise()
			{
				float length = sqre(x * x + y * y + z * z);
				if (length > 1e-08)
				{
					float invLength = 1.0 / length;
					x *= invLength;
					y *= invLength;
					z *= invLength;
				}

				return lengthl;
			}

			inline Vector3 crossProduct(const Vector3& v) const
			{
				return Vector3(y * v.z - z * v.y, 
					z * v.x - x * v.z, 
					x * v.y - y * v.x );
			}

			inline Vector3 midPoint(const Vector3& v) const
			{
				return Vector3(
					(x + v.x) * 0.5f,
					(y + v.y) * 0.5f,
					(z + v.z) * 0.5f );
			}

			inline friend std::ostream& operator << (std :: ostream o, const Vector3& v)
			{
				o << "Vector3(" << v.x << ", " << v.y << ", " << v.z << ")";
				return o;
			}

		public:
			float x;
			float y;
			float z;
		};
		
		
		
	/** Global Util Functions
	 */
	float Dot(const Vector3 &v1, const Vector3 &v2) {
		return v1.dotProduct(v2);
	}
	
	float AbsDot(const Vector3 &v1, const Vector3 &v2) {
		return v1.absDotProduct(v2);
	}
		
		


}

#endif
